﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.Windows.Forms;
using org.syncsoft.overalt.gui.entities;

namespace org.syncsoft.overalt.gui.socket
{
    /// <summary>
    /// Commands
    /// </summary>
    enum ECommand
    {
        AddFile,
        Update,
        Commit,
        Delete,
        ShareFolder,
        Login,
        GetComunities,
        GetContacts,
        GetModifiedFiles,
        GetMembers,
        Exception,
        GetFiles,
        GetHistoryFiles,
        GetSettingsFile
    }

    /// <summary>
    /// Channel communication whit socket
    /// </summary>
    public class SocketChannel : ISocketChannel
    {
        #region variables and methods
        private static ClientSocket clientS;
        private const string strToken = "#";
        private ArrayList communities;
        private ArrayList contacts;
        private ArrayList restrictions;
        private ArrayList histories;
        private ArrayList modifiedFiles;
        private ArrayList files;
        private string loginName;
        private ArrayList sendFiles;
        private ArrayList setingsFiles;
        private string confirmLogin;
        private static Thread thread;
        private ArrayList members;
        public static SocketChannel socket;
        private static bool error = false; 
        /// <summary>
        /// constructor - initialize clientSocket in IP "127.0.0.2" and Port 1000 
        /// and start thread eventlistening
        /// </summary>
        public SocketChannel()
        {
            if (clientS == null || !clientS.Connected)
            {
                clientS = new ClientSocket("127.0.0.2", 1000);
                clientS.Connect();
                WaitTime();
            }
            if (clientS.Connected)
            {
                thread = new Thread(EventListening);
                thread.Start();
            }
            else
            {
                error = true;
                throw new Exception("Falha na conexão!");
            }
        }

        /// <summary>
        ///  a unique instance of SocketChannel
        /// </summary>
        /// <returns>SocketChannel</returns>
        public static SocketChannel GetInstance()
        {
            if (clientS != null)
            {
                thread.Join(200);
                clientS.Disconnect();
                clientS = new ClientSocket("127.0.0.3", 1000);
                clientS.Connect();
                WaitTime();
                if (!clientS.Connected)
                {
                    error = true;
                    throw new Exception("Falha na conexão!");
                }

            }

            if (socket == null)
            {
                socket = new SocketChannel();
            }
            return socket;
        }



        /// <summary>
        /// listening new messages and receive for GUI
        /// </summary>
        public void EventListening()
        {
            bool temp = false;
            String msg = "";

            while (true)
            {
                msg = clientS.ReceivedText;

                temp = msg != "" && msg != null;

                if (temp)
                {
                    temp = false;
                    Receive(msg);
                }
                WaitTime();
            }
        }

        /// <summary>
        /// wait 500mm to don't consume CPU resources
        /// </summary>
        private static void WaitTime()
        {
            try
            {
                Thread.Sleep(500);
            }
            catch (ThreadInterruptedException ex)
            {
                throw new Exception("Thread aborted!");
            }
        }

        /// <summary>
        /// Send message for ServerSocket
        /// </summary>
        /// <param name="msg">message to send</param>
        /// <returns>boolean</returns>
        private bool Send(String msg)
        {
            bool tmp = clientS.SendText(msg);
            return tmp;
        }

        /// <summary>
        /// reeceive messsage from serverSocket
        /// </summary>
        /// <param name="msg">message received</param>
        private void Receive(String msg)
        {
            try
            {
                string command = msg.Split('#').First();
                ECommand cmd = (ECommand)int.Parse(command);

                String[] parameters = msg.Substring(command.Length + 1, msg.Length - (command.Length + 1)).Split('#');

                switch (cmd)
                {
                    case ECommand.GetComunities:
                        try
                        {
                            this.ToCollection(parameters, out communities);
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber comunidades.");
                        }
                        break;
                    case ECommand.GetModifiedFiles:
                        try
                        {
                            this.ToCollection(parameters, out modifiedFiles);
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber informações sobre os arquivos modificados.");
                        }
                        break;
                    case ECommand.GetContacts:
                        try
                        {
                            this.ToCollection(parameters, out contacts);
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber contatos.");
                        }
                        break;
                    case ECommand.Login:
                        try
                        {
                            confirmLogin = parameters[0];
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber confirmação de login.");
                        }
                        break;
                    case ECommand.GetFiles:
                        try
                        {
                            this.ToCollection(parameters, out files);
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber arquivos.");
                        }
                        break;
                    case ECommand.GetHistoryFiles:
                        try
                        {
                            this.ToCollection(parameters, out histories);
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber histórias.");
                        }
                        break;
                    case ECommand.GetSettingsFile:
                        try
                        {
                            this.ToCollection(parameters, out setingsFiles);
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber informações de arquivos.");
                        }
                        break;
                    case ECommand.GetMembers:
                        try
                        {
                            this.ToCollection(parameters, out members);
                        }
                        catch
                        {
                            throw new Exception("Falha ao receber membros.");
                        }
                        break;
                    case ECommand.Exception:
                        throw new Exception("" + parameters[0] + " : " + parameters[1]);
                        break;
                }
            }
            catch (Exception ex)
            {
                error = true;
                MessageBox.Show("Não foi possível receber informações devidamente. \nDetalhes: " + ex.Message);
                //clientS.Disconnect();
                //thread.Abort();
            }
        }
        #endregion variables and methods
        #region ISocketChannel Members

        /// <summary>
        /// 
        /// </summary>
        /// <param name="login"></param>
        /// <param name="senha"></param>
        /// <returns></returns>
        public bool Login(string login, string senha)
        {
            String msg = "";
            msg = ECommand.Login.GetHashCode() + strToken + login + strToken + senha;
            Send(msg);

            bool tmp = false;
            this.TimeOut(DateTime.Now, true);
            while (true)
            {
                if (confirmLogin != null && confirmLogin.Trim() != "")
                {
                    tmp = bool.Parse(confirmLogin);
                    break;
                }
                if (error)
                {
                    break;
                }
                if (TimeOut(DateTime.Now, false))
                {
                    break;
                }
       
                WaitTime();
            }
            return tmp;
        }

        DateTime time = new DateTime();
        public bool TimeOut(DateTime timeNow, bool init)
        {
            bool _break = false;

            if (!init)
            {
                if (timeNow > time)
                {
                    _break = true;
                    time.AddHours(1);
                    throw new Exception("Time out!");
                }
            }
            else
            {
                time = DateTime.Now.AddSeconds(15);
            }
            return _break;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        public void Update(File file)
        {
            try
            {
                String msg = "";
                msg = ECommand.Update.GetHashCode() + file.Path + "\\" + file.FileName;

                Send(msg);

            }
            catch
            {
                throw new Exception("Não foi possível atualizar o arquivo.");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="title"></param>
        /// <param name="description"></param>
        /// <param name="versionDescription"></param>
        /// <param name="communities"></param>
        /// <param name="permission"></param>
        /// <param name="tags"></param>
        public void Commit(File file)
        {
            String msg = "" + ECommand.Commit.GetHashCode();

            if (file.ConstraintType != null)
            {
                if (String.IsNullOrEmpty(file.Description))
                {
                    msg += strToken + file.Path;
                }
                else
                {
                    msg += strToken + file.Path + strToken + file.Description;
                }
            }
            else
            {
                if (file.TypeSync == null)
                {
                    TypeSync tp = new TypeSync();
                    tp.Id = tp.ListTypeSync().First().Id;
                    file.TypeSync = tp;
                }

                if (file.FlagUploadDownload == null)
                {
                    file.FlagUploadDownload = "0";
                }

                if (file.TypeSync == null)
                {
                    ConstraintType tp = new ConstraintType();
                    tp.Id = tp.ListConstraintType().First().Id;
                    file.ConstraintType = tp;
                }

                if (file.Community == null)
                {
                    
                }
                msg += strToken + StringCommunityCollection(file.Community) +
                strToken + file.ConstraintType.Id +
                strToken + file.Description +
                strToken + file.FileName +
                strToken + file.FlagUploadDownload +
                strToken + file.Path +
                strToken + file.Tags +
                strToken + file.Title +
                strToken + file.TypeSync.Id +
                strToken + file.VersionDescription;

            }

            Send(msg);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="title"></param>
        /// <param name="description"></param>
        /// <param name="communities"></param>
        /// <param name="tags"></param>
        /// <param name="permission"></param>
        public void AddFile(File file)
        {
            String msg = "";
            msg = ECommand.AddFile.GetHashCode() +
                strToken + StringCommunityCollection(file.Community) +
                strToken + file.ConstraintType.Id +
                strToken + file.Description +
                strToken + file.FileName +
                strToken + file.FlagUploadDownload +
                strToken + file.Path +
                strToken + file.Tags +
                strToken + file.Title +
                strToken + file.TypeSync.Id +
                strToken + file.VersionDescription;


            Send(msg);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        public void Delete(File file)
        {
            String msg = "";
            msg = ECommand.Delete.GetHashCode() + strToken + file.Path + file.FileName;
            Send(msg);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="folderPath"></param>
        /// <param name="typeShare"></param>
        /// <param name="autoFillTitleDesc"></param>
        /// <param name="contacts"></param>
        /// <param name="community"></param>
        /// <param name="members"></param>
        public void ShareFolder(Folder folder)
        {
            String msg = "";
            msg = ECommand.ShareFolder.GetHashCode() +
                strToken + folder.AutoFillTitleDescription.ToString() +
                //strToken + folder.Community.Description +
                //strToken + folder.Community.Id +
                //strToken + StringContactsCollection(folder.Contacts) +
                //strToken + StringMembersCollection(folder.Members) +
                strToken + folder.Path;// +
                //strToken + folder.TypeSharing.Id;

            Send(msg);
        }

        /// <summary>
        /// change array in string collection 
        /// </summary>
        /// <param name="array">object array</param>
        /// <returns>string collection</returns>
        public string StringCollection(Object array)
        {

            string token = "%";
            string collection = token;
            foreach (string str in (array as ICollection))
            {
                collection += str;
                collection += token;
            }
            return collection;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="communities"></param>
        /// <returns></returns>
        private string StringCommunityCollection(List<Community> communities)
        {
            string collection = "";

            foreach (Community community in communities)
            {
                collection += "," + community.Description + "," + community.Id;
            }
            return collection;
        }
        private string StringContactsCollection(List<Contacts> contacts)
        {
            string collection = "";

            foreach (Contacts contact in contacts)
            {
                collection += "," + contact.Description + "," + contact.Id;
            }
            return collection;
        }
        private string StringMembersCollection(List<Members> members)
        {
            string collection = "";

            foreach (Members member in members)
            {
                collection += "," + member.Name + "," + member.Id;
            }
            return collection;
        }
        //StringCommunityCollection
        /// <summary>
        /// 
        /// </summary>
        /// <param name="list"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        //public List<Object> ConvertCollection(ArrayList list, Object type)
        //{
        //    List<Object> list;
        //    type m;
        //    for (int i = 0; i < list.Count; i++)
        //    {
        //        m = new Members();
        //        string[] member = list[i].ToString().Substring(1, list[i].ToString().Length - 1);
        //        m.Id = member[0];
        //        m.Name = member[1];
        //        list.Add(m);
        //    }
        //    return list;
        //}
        /// <summary>
        /// populate ArrayList with string collection
        /// </summary>
        /// <param name="str">string []</param>
        /// <param name="array">out ArrayList</param>
        public void ToCollection(string[] str, out ArrayList array)
        {
            //
            ICollection collection = str[0].Substring(1, str[0].Length - 1).Split('%');
            array = new ArrayList();

            if (collection.Count > 0)
            {
                array.AddRange(collection as ICollection);
            }
        }

        /// <summary>
        /// getComunities
        /// </summary>
        /// <param name="login">user login</param>
        /// <returns>all comuniites for user</returns>
        public List<Community> GetComunities()
        {
            string msg = "";
            msg = ECommand.GetComunities.GetHashCode() + "" + strToken;
            Send(msg);

            List<Community> list = new List<Community>();
            Community community;
            TimeOut(DateTime.Now, true);
            while (true)
            {
                if (communities != null && communities.Count > 0)
                {
                    for (int i = 0; i < communities.Count-1; i += 2)
                    {
                        community = new Community();
                        community.Description = communities[i].ToString();
                        community.Id = communities[i+1].ToString();
                        community.Name = communities[i+2].ToString();
                        
                        list.Add(community);
                    }
                    break;
                }
                if (error)
                {
                    break;
                }
                if (TimeOut(DateTime.Now, false))
                {
                    break;
                }
                WaitTime();
            }
            if (communities != null)
            {
                communities = null;
            }
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="login"></param>
        /// <returns></returns>
        public List<Contacts> GetContacts()
        {
            string msg = "";
            msg = ECommand.GetContacts.GetHashCode() + strToken;
            Send(msg);

            List<Contacts> list = new List<Contacts>();
            Contacts contact;
            //TimeOut(DateTime.Now, true);
            while (true)
            {
                if (contacts != null && contacts.Count > 0)
                {
                    for (int i = 0; i < contacts.Count-1; i += 2)
                    {
                        contact = new Contacts();
                        contact.Description = contacts[i].ToString();
                        contact.Id = contacts[i+1].ToString();
                        
                        list.Add(contact);
                    }
                    break;
                }
                if (error)
                {
                    break;
                }
                if (TimeOut(DateTime.Now, false))
                {
                    break;
                }
                WaitTime();
            }
            if (contacts != null)
            {
                contacts = null;
            }
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="community"></param>
        /// <returns></returns>
        public List<Members> GetMembers(Community community)
        {
            string msg = "";
            msg = ECommand.GetMembers.GetHashCode() + strToken + community.Id;
            Send(msg);

            List<Members> list = new List<Members>();
            Members member;
            //TimeOut(DateTime.Now, true);
            while (true)
            {
                if (members != null && members.Count > 0)
                {
                    for (int i = 0; i < members.Count-1; i += 2)
                    {
                        member = new Members();
                        member.Name = members[i].ToString();
                        member.Id = members[i+1].ToString();

                        list.Add(member);
                    }
                    break;
                }
                if (error)
                {
                    break;
                }
                if (TimeOut(DateTime.Now, false))
                {
                    break;
                }
                WaitTime();
            }
            if (members != null)
            {
                members = null;
            }
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<File> GetModifiedFiles(Folder folder)
        {
            string msg = "";
            msg = ECommand.GetModifiedFiles.GetHashCode() + strToken + folder.Path;
            Send(msg);

            List<File> list = new List<File>();
            File file;

            TimeOut(DateTime.Now, true);
            while (true)
            {
                if (modifiedFiles != null && modifiedFiles.Count > 0)
                {
                    for (int i = 0; i < modifiedFiles.Count; i += 2)
                    {
                        file = new File();
                        file.Description = modifiedFiles[0].ToString();
                        file.FileName = modifiedFiles[1].ToString();
                        file.Path = modifiedFiles[2].ToString();
                        file.Title = modifiedFiles[3].ToString();
                        
                        String[] communities = modifiedFiles[4].ToString().Substring(1, modifiedFiles[4].ToString().Length-2).Split('@');
                        file.Community = new List<Community>();
                        for(int j =0; j<communities.Length; j+=2)
                        {
                            Community c = new Community();
                            c.Id = communities[j];
                            c.Description = communities[j+1];
                            file.Community.Add(c);
                        }
                       
                        file.Tags = modifiedFiles[5].ToString();
                        //Members m = new Members();
                        //String[] members  = modifiedFiles[7].ToString().Split('%');
                        //for(int i =0; i<members.Length; i+=2)
                        //{
                        //    c.Description = members[i];
                        //    c.Id = members[i+1];
                            
                        //}
                        file.FlagUploadDownload = "0";
                        
                        file.VersionDescription = modifiedFiles[6].ToString();

                        list.Add(file);
                    }
                    break;
                }
                if (error)
                {
                    break;
                }
                if (TimeOut(DateTime.Now, false))
                {
                    break;
                }
                WaitTime();
            }
            if (modifiedFiles != null)
            {
                modifiedFiles = null;
            }
            return list;
        }


        /// <summary>
        /// recebe os historico dos arquivos 
        /// </summary>
        /// <returns></returns>
        public List<File> GetHistoryFiles(Folder folder)
        {
            string msg = "";
            msg = ECommand.GetHistoryFiles.GetHashCode() + strToken + folder.Path;
            Send(msg);

            List<File> list;
            while (true)
            {
                if (histories != null && histories.Count > 0)
                {
                    list = (List<File>)histories.Clone();
                    histories = null;
                    break;
                }
                WaitTime();
            }
            if (histories != null)
            {
                histories = null;
            }
            return list;

        }

        public List<File> GetFiles(Folder folder)
        {
            string msg = "";
            msg = ECommand.GetFiles.GetHashCode() + strToken + folder.Path;
            Send(msg);

            List<File> list;
            while (true)
            {
                if (files != null && files.Count > 0)
                {
                    list = (List<File>)files.Clone();
                    files = null;
                    break;
                }
                WaitTime();
            }
            if (files != null)
            {
                files = null;
            }
            return list;
        }
        public File GetSettingsFile(File file)
        {
            return null;
        }


        #endregion


    }

    /// <summary>
    /// Exception SocketChannelException
    /// </summary>
    public class SocketChannelException : Exception
    {
        public SocketChannelException(string msg)
        {

        }
    }
}
